# Coloured Petri Net (CPN): Transition Class
# Release: Development
#
# Copyright (c) 2002 Torvald Riegel. All rights reserved. This program is
# free software; you can redistribute it and/or modify it under the same
# terms as Perl itself.
#
# Copyright (c) 2011 Brent R. Scott. All rights reserved. This program has
# been modified from the original release and is free software. You can 
# redistribute it and/or modify it under the same terms as Perl itself.

package CPN::Transition;

use strict;
use Carp;
use Data::Dumper;

sub new {
    my $type = shift;
    my $name = '';
    my %params = @_;
    $name = $params{'name'} if defined $params{'name'};
    return bless {
    	          name => $name,
                  arcsIn => {},
                  arcsOut => {},
                  guard => undef,
                  code => undef,
                  net => undef,
                  cacheOut => [],
                  cacheIn => [],
                  isActive => 0,
                 }, $type;
}
sub destroy { 
	my $self = shift;
	printf("$self dying at %s\n", scalar localtime);
}

# Params: Reference to associated to Petri Net
sub setNet {
    my ($self) = shift;
    $self->{net} = shift;
}

#########################
# Input Arc Accessors	#
#########################

# Associate a Place and Input Arc with Transition
sub setArcIn {
    my ($self, $place, $condition, $arc) = @_;
    my $placeRef = $self->{net}->{places}->{$place}
      if (not UNIVERSAL::isa($place, 'CPN::Place')) and
         defined ($self->{net});
    if (not defined $arc) {
    	# Create New Arc
        $arc=CPN::Transition::ArcIn->new();
    }
    if (UNIVERSAL::isa($arc, 'CPN::Transition::ArcIn')) {
      # Assign Arc as Input from Place
      $self->{arcsIn}->{$place} = $arc;
      push @{$placeRef->{arcs}}, $arc;
      # Informs the Net of a Place Transition Relationship
      push @{$self->{net}->{placeTransitionRel}->{$place}}, $self->{name}
        if defined ($self->{net});
      $arc->setCondition($condition);
      $arc->setPlace($placeRef);
      $arc->setTransition($self);
    }
      
}

sub getArcIn {
    my ($self, $place) = @_;
    croak "no arc from this place" unless $self->existsArcIn($place);
    return $self->{arcsIn}->{$place};
}

sub getInputPlaces {
    my $self = shift;
    return keys %{$self->{arcsIn}};
}

sub existsArcIn {
    my ($self, $place) = @_;
    return exists $self->{arcsIn}->{$place};
}

#########################
# Output Arc Accessors	#
#########################

# Associate a Place and Output Arc with Transition
sub setArcOut {
    my ($self, $place, $expr, $arc) = @_;
    my $placeRef = $self->{net}->{places}->{$place}
      if (not UNIVERSAL::isa($place, 'CPN::Place')) and
         defined ($self->{net});
    if (not defined $arc) {
    	# Create New Arc
        $arc=CPN::Transition::ArcOut->new();
    }

    if (UNIVERSAL::isa($arc, 'CPN::Transition::ArcOut')) {
      # Assign Arc as Output from Place
      $self->{arcsOut}->{$place} = $arc;
      # Informs the Net of a Place Transition Relationship
      push @{$self->{net}->{placeTransitionRel}->{$place}}, $self->{name}
        if defined ($self->{net});
      $arc->setExpr($expr);
      $arc->setPlace($placeRef);
      $arc->setTransition($self);
    }
}

sub getArcOut {
    my ($self, $place) = @_;
    croak "no arc to this place" unless $self->existsArcOut($place);
    return $self->{arcsOut}->{$place};
}

sub getOutputPlaces {
    my $self = shift;
    return keys %{$self->{arcsOut}};
}

sub existsArcOut {
    my ($self, $place) = @_;
    return exists $self->{arcsOut}->{$place};
}


#########################
# Guard/Code Setter		#
#########################

sub setGuard {
    my $self = shift;
    $self->{guard} = shift;
}

sub setCode {
    my $self = shift;
    $self->{code} = shift;
}


#########################
# Execute Step Helper	#
#########################

# Returns: True if a Step was Executed
sub executeStep {
    my ($self) = @_;

    return if not $self->{isActive};

    # TODO: Rewrite Step Execution

    # Goes into Inactive State
    $self->{isActive} = 0;
    return 0;
}



# notifies the transition about a new token on a place
# params: place, token ids
sub tokenAdded {
    my ($self, $place, @tokens) = @_;

    # ignores the notifications unless there is an input arc from the place
    # should not happen (net->placeTransitionRel)
    #return unless exists $self->{arc_in}->{$place};
    # notifies the arc
    $self->{arcsIn}->{$place}->tokenAdded(@tokens);
    # move the arc with the new token to the end of the arc vector if in
    # noSteps state
    if ($self->{isActive}) {
        # TODO: sorting again should be better
    }
    # goes out of noSteps state
    # TODO: More checks for all Arcs
    $self->{noSteps} = 0;
}


# Notifies the transition about removed tokens
# Params: Place, Token IDs
sub tokenRemoved {
    my ($self, $place, @tokens) = @_;

    # ignores the notifications unless there is an input arc from the place
    # shouldnt happen: return unless exists $self->{arcsIn}->{$place};
    # notifies the arc
    $self->{arcsIn}->{$place}->tokenRemoved(@tokens);
}

1;
